package com.xiaoshuidi.cloud.module.finance.job.voucher;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.tenant.core.job.TenantJob;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.module.contract.api.ContractRentOrderApi;
import com.xiaoshuidi.cloud.module.contract.vo.OrderFeeDetailVO;
import com.xiaoshuidi.cloud.module.contract.vo.RentOrderExtVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.voucher.vo.FinanceVoucherDetailRespVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.voucher.vo.FinanceVoucherSummaryVO;
import com.xiaoshuidi.cloud.module.finance.convert.voucher.FinanceVoucherDetailConvert;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.certificate.FinanceCertificateConfigDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.voucher.FinanceVoucherDetailDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.voucher.FinanceVoucherInfoDO;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.certificate.FinanceCertificateConfigMapper;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.voucher.FinanceVoucherDetailMapper;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.voucher.FinanceVoucherInfoMapper;
import com.xiaoshuidi.cloud.module.finance.enums.voucher.*;
import com.xiaoshuidi.cloud.module.finance.service.invoice.FinanceInvoiceRecordService;
import com.xiaoshuidi.cloud.module.finance.service.voucher.FinanceVoucherService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.math.RoundingMode;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author dujianguang
 */
@Slf4j
@TenantJob
@Component
public class VoucherJob {

	@Resource
	private ContractRentOrderApi contractRentOrderApi;
	@Resource
	private FinanceVoucherInfoMapper financeVoucherInfoMapper;
	@Resource
	private FinanceVoucherDetailMapper financeVoucherDetailMapper;
	@Resource
	private FinanceCertificateConfigMapper financeCertificateConfigMapper;
	@Resource
	private FinanceVoucherService financeVoucherService;

	private static final AtomicLong VOUCHER_NO_SEQ = new AtomicLong(0L);


	/**
	 * 凭证信息汇总
	 */
	@XxlJob("voucherDateInsert")
	public void voucherDateInsert() {
		try{
			Calendar calendar = Calendar.getInstance();
			calendar.add(Calendar.DATE, -1);
			Date startDate = getStartOfDay(calendar.getTime());
			Date endDate = getEndOfDay(startDate);

			String startTime = DateUtil.format(getStartOfDay(startDate), DatePattern.NORM_DATETIME_PATTERN);
			String endTime = DateUtil.format(getEndOfDay(endDate), DatePattern.NORM_DATETIME_PATTERN);
			log.info("凭证定时任务 =========="+startTime+"======"+endTime+"====================");
			//通过时间查询付款成功的账单
			CommonResult<List<RentOrderExtVO>> rentOrderList = contractRentOrderApi.getPaymentRentOrderList(startTime, endTime);
			log.info("查询账单结果："+ JSONObject.toJSONString(rentOrderList));
			List<RentOrderExtVO> temp = Optional.ofNullable(rentOrderList.getCheckedData()).orElse(new ArrayList<>());
			//查询有凭证配置的门店
			List<FinanceCertificateConfigDO> certificateConfigDOList = financeCertificateConfigMapper.selectList()
					.stream().filter(it-> it.getApartmentId()!=null && !it.getDeleted()).collect(Collectors.toList());
			log.info("配置列表："+ certificateConfigDOList.size());
			final Map<Long, FinanceCertificateConfigDO> oneConfigMap = Optional.ofNullable(certificateConfigDOList).orElse(new ArrayList<>()).stream().collect(Collectors.toMap(FinanceCertificateConfigDO::getApartmentId, a -> a, (k1, k2) -> k2));
			List<RentOrderExtVO> resultList = temp.stream().filter(it-> oneConfigMap.containsKey(it.getApartmentId())).collect(Collectors.toList());
			List<FinanceVoucherSummaryVO> voList = new ArrayList<>();
			// 按公寓
			Map<Long,List<RentOrderExtVO>> tempMap = resultList.stream().collect(Collectors.groupingBy(RentOrderExtVO::getApartmentId));
			log.info("按公寓分账单列表 {}", JSONObject.toJSONString(tempMap.keySet()));
			tempMap.forEach((k,v) -> {
				FinanceCertificateConfigDO configDO = oneConfigMap.get(k);
				// 按支付方式
				Map<String, List<RentOrderExtVO>> paymentMap = v.stream().collect(Collectors.groupingBy(order -> {
					if (FinanceVoucherPaymentTypeEnum.YHZZ.getName().equals(order.getPayMethod()) || FinanceVoucherPaymentTypeEnum.XJ.getName().equals(order.getPayMethod())) {
						return order.getPayMethod();
					} else {
						return FinanceVoucherPaymentTypeEnum.DSFZF.getName();
					}
				}));
				log.info("按按支付方式分账单列表 {}", JSONObject.toJSONString(paymentMap.keySet()));
				FinanceVoucherSummaryVO financeVoucherSummaryVO = voucherSummaryVOConvert("所有渠道收款", endDate, configDO, FinanceVoucherTypeEnum.VOUCHER_TYPE_ZZ.getCode());
				List<FinanceVoucherDetailRespVO> items = new ArrayList<>();
				paymentMap.forEach((k1, v1) -> {
					//明细
					List<OrderFeeDetailVO> feesList = new ArrayList<>();
					for (RentOrderExtVO vo : v1) {
						feesList.addAll(vo.getFeeDetail());
					}
					BigDecimal amount = feesList.stream().map(it->it.getAmount()).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
					//借款分录
					FinanceVoucherDetailRespVO vo1 = FinanceVoucherDetailRespVO.builder()
							.entryDc(FinanceVoucherJieDaiTypeEnum.JIE.getCode())
							.voucherAbstract(configDO.getApartmentName() + endTime + "收款")
							.debitAmount(BigDecimal.ZERO)
							.originalAmount(amount)
							//金融机构
							.accountProject1(configDO.getAccountProject1())
							.accountProjectCode1(configDO.getAccountProjectCode1())
							.accountProjectName1(configDO.getAccountProjectName1())
							.build();
					if (k1.equals(FinanceVoucherPaymentTypeEnum.DSFZF.getName())) {
						vo1.setFeeName(FinanceVoucherPaymentTypeEnum.DSFZF.getName());
						vo1.setAccountName(FinanceVoucherPaymentTypeEnum.DSFZF.getRemark());
						vo1.setAccountNumber(FinanceVoucherPaymentTypeEnum.DSFZF.getCode());
					}
					if (k1.equals(FinanceVoucherPaymentTypeEnum.YHZZ.getName())) {
						vo1.setAccountProject2(configDO.getAccountProject5());
						vo1.setAccountProjectCode2(configDO.getAccountProjectCode5());
						vo1.setAccountProjectName1(configDO.getAccountProjectName5());
						vo1.setFeeName(FinanceVoucherPaymentTypeEnum.YHZZ.getName());
						vo1.setAccountName(FinanceVoucherPaymentTypeEnum.YHZZ.getRemark());
						vo1.setAccountNumber(FinanceVoucherPaymentTypeEnum.YHZZ.getCode());
					}
					if (k1.equals(FinanceVoucherPaymentTypeEnum.XJ.getName())) {
						vo1.setFeeName(FinanceVoucherPaymentTypeEnum.XJ.getName());
						vo1.setAccountName(FinanceVoucherPaymentTypeEnum.XJ.getRemark());
						vo1.setAccountNumber(FinanceVoucherPaymentTypeEnum.XJ.getCode());
					}
					items.add(vo1);

				});
				log.info("收款分录 {}", JSONObject.toJSONString(items));
				// 贷款 按类型汇总
				List<OrderFeeDetailVO> feesList = new ArrayList<>();
				for (RentOrderExtVO vo : v) {
					feesList.addAll(vo.getFeeDetail());
				}
				Map<String, List<OrderFeeDetailVO>> feesMap = feesList.stream().filter(it-> BigDecimal.ZERO.compareTo(it.getAmount()) < 0)
						.collect(Collectors.groupingBy(vo -> FinanceVoucherFeeTypeMapperEnum.find(vo.getFeeName()).getKdName()));
				log.info("按类型分账单列表 {}", JSONObject.toJSONString(feesMap.keySet()));

				feesMap.forEach((k2, v2) -> {
					log.info("{}  {}", k2, FinanceVoucherFeeTypeEnum.find(k2).getRemark());
					FinanceVoucherDetailRespVO item = FinanceVoucherDetailRespVO.builder()
							.entryDc(FinanceVoucherJieDaiTypeEnum.DAI.getCode())
							.feeName(k2)
							.accountName(FinanceVoucherFeeTypeEnum.find(k2).getRemark())
							.accountNumber(FinanceVoucherFeeTypeEnum.find(k2).getCode())
							.voucherAbstract(configDO.getApartmentName() + endTime + k2)
							.originalAmount(BigDecimal.ZERO)
							.debitAmount(v2.stream().map(OrderFeeDetailVO::getAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO))
							//客户
							.accountProject1(configDO.getAccountProject1())
							.accountProjectCode1(configDO.getAccountProjectCode1())
							.accountProjectName1(configDO.getAccountProjectName1())
							//租赁项目
							.accountProject2(configDO.getAccountProject2())
							.accountProjectCode2(configDO.getAccountProjectCode2())
							.accountProjectName2(configDO.getAccountProjectName2())
							//物业地址
							.accountProject3(configDO.getAccountProject3())
							.accountProjectCode3(configDO.getAccountProjectCode3())
							.accountProjectName3(configDO.getAccountProjectName3())
							.build();
					//租金
					if (FinanceVoucherFeeTypeMapperEnum.ZUJIN_FEES.getKdName().equals(k2)) {
						BigDecimal debit = item.getDebitAmount();
						item.setDebitAmount(BigDecimal.valueOf(0.6).multiply(debit).setScale(2, BigDecimal.ROUND_HALF_UP));
						FinanceVoucherDetailRespVO item1 = FinanceVoucherDetailRespVO.builder()
								.entryDc(FinanceVoucherJieDaiTypeEnum.DAI.getCode())
								.feeName(FinanceVoucherFeeTypeMapperEnum.GUANLI_FEES.getKdName())
								.accountName(FinanceVoucherFeeTypeEnum.find(FinanceVoucherFeeTypeMapperEnum.GUANLI_FEES.getKdName()).getRemark())
								.accountNumber(FinanceVoucherFeeTypeEnum.find(FinanceVoucherFeeTypeMapperEnum.GUANLI_FEES.getKdName()).getCode())
								.voucherAbstract(configDO.getApartmentName() + endTime + FinanceVoucherFeeTypeMapperEnum.GUANLI_FEES.getKdName())
								.originalAmount(BigDecimal.ZERO)
								.debitAmount(debit.subtract(item.getDebitAmount()))
								//客户
								.accountProject1(configDO.getAccountProject1())
								.accountProjectCode1(configDO.getAccountProjectCode1())
								.accountProjectName1(configDO.getAccountProjectName1())
								//租赁项目
								.accountProject2(configDO.getAccountProject2())
								.accountProjectCode2(configDO.getAccountProjectCode2())
								.accountProjectName2(configDO.getAccountProjectName2())
								//物业地址
								.accountProject3(configDO.getAccountProject3())
								.accountProjectCode3(configDO.getAccountProjectCode3())
								.accountProjectName3(configDO.getAccountProjectName3())
								.build();
						items.add(item1);
					}
					items.add(item);
				});

				//已开发票
				List<RentOrderExtVO> invoiceList = v.stream().filter(entity -> entity.getInvoiceStatus() == 2).collect(Collectors.toList());
				if (!CollectionUtils.isEmpty(invoiceList)) {
					//总借款分录
					FinanceVoucherDetailRespVO jie1 = FinanceVoucherDetailRespVO.builder()
							.entryDc(FinanceVoucherJieDaiTypeEnum.JIE.getCode())
							.voucherAbstract(configDO.getApartmentName() + endTime + "收款")
							.debitAmount(BigDecimal.ZERO)
							.originalAmount(invoiceList.stream().map(RentOrderExtVO::getTotalAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO))
							//金融机构
							.accountProject1(configDO.getAccountProject1())
							.accountProjectCode1(configDO.getAccountProjectCode1())
							.accountProjectName1(configDO.getAccountProjectName1())
							//银行账户
							.accountProject2(configDO.getAccountProject5())
							.accountProjectCode2(configDO.getAccountProjectCode5())
							.accountProjectName2(configDO.getAccountProjectName5())
							.build();
					items.add(jie1);
					//总贷款分录
					FinanceVoucherDetailRespVO dai1 = FinanceVoucherDetailRespVO.builder()
							.entryDc(FinanceVoucherJieDaiTypeEnum.DAI.getCode())
							.voucherAbstract(configDO.getApartmentName() + endTime + "收款")
							.originalAmount(BigDecimal.ZERO)
							.debitAmount(invoiceList.stream().map(RentOrderExtVO::getTotalAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO))
							//金融机构
							.accountProject1(configDO.getAccountProject1())
							.accountProjectCode1(configDO.getAccountProjectCode1())
							.accountProjectName1(configDO.getAccountProjectName1())
							.build();
					items.add(dai1);

					// 按类型汇总
					List<OrderFeeDetailVO> invoiceFeesList = new ArrayList<>();
					for (RentOrderExtVO vo : invoiceList) {
						invoiceFeesList.addAll(vo.getFeeDetail());
					}
					Map<String, List<OrderFeeDetailVO>> invoiceFeesMap = invoiceFeesList.stream().collect(Collectors.groupingBy(vo -> FinanceVoucherFeeTypeMapperEnum.find(vo.getFeeName()).getKdName()));
					invoiceFeesMap.forEach((k2, v2) -> {
						FinanceVoucherDetailRespVO jie = FinanceVoucherDetailRespVO.builder()
								.entryDc(FinanceVoucherJieDaiTypeEnum.JIE.getCode())
								.feeName(k2)
								.accountName(FinanceVoucherFeeTypeEnum.find(k2).getRemark())
								.accountNumber(FinanceVoucherFeeTypeEnum.find(k2).getCode())
								.voucherAbstract(configDO.getApartmentName() + endTime + k2)
								.debitAmount(BigDecimal.ZERO)
								.originalAmount(v2.stream().map(OrderFeeDetailVO::getAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO))
								//客户
								.accountProject1(configDO.getAccountProject1())
								.accountProjectCode1(configDO.getAccountProjectCode1())
								.accountProjectName1(configDO.getAccountProjectName1())
								//租赁项目
								.accountProject2(configDO.getAccountProject2())
								.accountProjectCode2(configDO.getAccountProjectCode2())
								.accountProjectName2(configDO.getAccountProjectName2())
								//物业地址
								.accountProject3(configDO.getAccountProject3())
								.accountProjectCode3(configDO.getAccountProjectCode3())
								.accountProjectName3(configDO.getAccountProjectName3())
								.build();
						FinanceVoucherDetailRespVO dai = FinanceVoucherDetailRespVO.builder()
								.entryDc(FinanceVoucherJieDaiTypeEnum.DAI.getCode())
								.feeName(k2)
								.accountName(FinanceVoucherFeeTypeEnum.find(k2).getRemark())
								.accountNumber(FinanceVoucherFeeTypeEnum.find(k2).getCode())
								.voucherAbstract(configDO.getApartmentName() + endTime + k2)
								.originalAmount(BigDecimal.ZERO)
								//客户
								.accountProject1(configDO.getAccountProject1())
								.accountProjectCode1(configDO.getAccountProjectCode1())
								.accountProjectName1(configDO.getAccountProjectName1())
								//租赁项目
								.accountProject2(configDO.getAccountProject2())
								.accountProjectCode2(configDO.getAccountProjectCode2())
								.accountProjectName2(configDO.getAccountProjectName2())
								//物业地址
								.accountProject3(configDO.getAccountProject3())
								.accountProjectCode3(configDO.getAccountProjectCode3())
								.accountProjectName3(configDO.getAccountProjectName3())
								.build();
						FinanceVoucherDetailRespVO tax = FinanceVoucherDetailRespVO.builder()
								.entryDc(FinanceVoucherJieDaiTypeEnum.DAI.getCode())
								.feeName(k2)
								.accountName(FinanceVoucherFeeTypeEnum.find(k2).getRemark())
								.accountNumber(FinanceVoucherFeeTypeEnum.find(k2).getCode())
								.voucherAbstract(configDO.getApartmentName() + endTime + k2)
								.originalAmount(BigDecimal.ZERO)
								//客户
								.accountProject1(configDO.getAccountProject1())
								.accountProjectCode1(configDO.getAccountProjectCode1())
								.accountProjectName1(configDO.getAccountProjectName1())
								//租赁项目
								.accountProject2(configDO.getAccountProject2())
								.accountProjectCode2(configDO.getAccountProjectCode2())
								.accountProjectName2(configDO.getAccountProjectName2())
								//物业地址
								.accountProject3(configDO.getAccountProject3())
								.accountProjectCode3(configDO.getAccountProjectCode3())
								.accountProjectName3(configDO.getAccountProjectName3())
								.build();
						//租金9%
						if (FinanceVoucherFeeTypeMapperEnum.ZUJIN_FEES.getKdName().equals(k2)) {
							jie.setOriginalAmount(BigDecimal.valueOf(0.6).multiply(jie.getDebitAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
							tax.setDebitAmount(getAfterTaxSum(jie.getOriginalAmount(), BigDecimal.valueOf(0.09)));
							dai.setDebitAmount(jie.getOriginalAmount().subtract(tax.getDebitAmount()));
						}
						//管理费6%
						if (FinanceVoucherFeeTypeMapperEnum.GUANLI_FEES.getKdName().equals(k2)) {
							jie.setOriginalAmount(BigDecimal.valueOf(0.6).multiply(jie.getDebitAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
							tax.setDebitAmount(getAfterTaxSum(jie.getOriginalAmount(), BigDecimal.valueOf(0.06)));
							dai.setDebitAmount(jie.getOriginalAmount().subtract(tax.getDebitAmount()));
						}
						//水费3%
						if (FinanceVoucherFeeTypeMapperEnum.LENGSHUIFEI_FEES.getKdName().equals(k2)) {
							tax.setDebitAmount(getAfterTaxSum(jie.getOriginalAmount(), BigDecimal.valueOf(0.03)));
							dai.setDebitAmount(jie.getOriginalAmount().subtract(tax.getDebitAmount()));
						}
						//电费13%
						if (FinanceVoucherFeeTypeMapperEnum.DIANFEI_FEES.getKdName().equals(k2)) {
							tax.setDebitAmount(getAfterTaxSum(jie.getOriginalAmount(), BigDecimal.valueOf(0.13)));
							dai.setDebitAmount(jie.getOriginalAmount().subtract(tax.getDebitAmount()));
						}
						//其他零星收入 6%
						if (FinanceVoucherFeeTypeMapperEnum.YUFUFEI_FEES.getKdName().equals(k2)) {
							tax.setDebitAmount(getAfterTaxSum(jie.getOriginalAmount(), BigDecimal.valueOf(0.06)));
							dai.setDebitAmount(jie.getOriginalAmount().subtract(tax.getDebitAmount()));
						}
						items.add(jie);
						items.add(dai);
						items.add(tax);
					});
				}
				financeVoucherSummaryVO.setDetailRespVOList(items);
				log.info("凭证数据 {}", JSONObject.toJSONString(items));
				voList.add(financeVoucherSummaryVO);
			});
			log.info("主体数据 {}", JSONObject.toJSONString(voList));
			for (FinanceVoucherSummaryVO vo : voList) {
				List<FinanceVoucherDetailRespVO> financeVoucherDetailRespVOS = vo.getDetailRespVOList();
				int number = 1;
				for (int i = 0; i < financeVoucherDetailRespVOS.size(); i++) {
					financeVoucherDetailRespVOS.get(i).setSeq(number);
					number += 1;
				}
				financeVoucherDetailRespVOS = financeVoucherDetailRespVOS.stream().sorted(Comparator.comparing(FinanceVoucherDetailRespVO::getSeq)).collect(Collectors.toList());
				vo.setDetailRespVOList(financeVoucherDetailRespVOS);
			}
			//组装唯一编码
			voList.forEach(entity -> entity.setSeqNo(voucherNo()));
			List<FinanceVoucherInfoDO> voucherInfoDOList = summaryConvertToInfo(voList);
			financeVoucherInfoMapper.insertBatch(voucherInfoDOList);
			Map<String, Long> seqMap = voucherInfoDOList.stream().collect(Collectors.toMap(FinanceVoucherInfoDO::getSeqNo, FinanceVoucherInfoDO::getId));
			List<FinanceVoucherDetailDO> voucherDetailDOList = detailVosConvertToDo(seqMap, voList);
			financeVoucherDetailMapper.insertBatch(voucherDetailDOList);

			//上传凭证
			seqMap.values().stream().forEach(id ->{
				financeVoucherService.syncVoucher(id);
			});

		}catch (Exception e){
			log.error("凭证生成异常", e);
		}
	}

	//组装凭证明细插入的实体类
	private List<FinanceVoucherDetailDO> detailVosConvertToDo(Map<String, Long> seqMap, List<FinanceVoucherSummaryVO> summaryVOList) {
		List<FinanceVoucherDetailDO> list = new ArrayList<>();
		for (FinanceVoucherSummaryVO financeVoucherSummaryVO : summaryVOList) {
			Long voucherId = seqMap.get(financeVoucherSummaryVO.getSeqNo());
			financeVoucherSummaryVO.getDetailRespVOList().forEach(entity -> entity.setVoucherId(voucherId));
			List<FinanceVoucherDetailDO> detailDOList = FinanceVoucherDetailConvert.INSTANCE.convert1(financeVoucherSummaryVO.getDetailRespVOList());
			list.addAll(detailDOList);
		}
		return list;
	}

	//组装凭证信息插入的实体类
	private List<FinanceVoucherInfoDO> summaryConvertToInfo(List<FinanceVoucherSummaryVO> summaryVOList) {
		List<FinanceVoucherInfoDO> list = Optional.ofNullable(summaryVOList).orElse(new ArrayList<>()).stream().map(entity -> {
			FinanceVoucherInfoDO financeVoucherInfoDO = new FinanceVoucherInfoDO();
			financeVoucherInfoDO.setSeqNo(entity.getSeqNo());
			financeVoucherInfoDO.setApartmentId(entity.getApartmentId());
			financeVoucherInfoDO.setApartmentName(entity.getApartmentName());
			financeVoucherInfoDO.setCompanyCode(entity.getCompanyCode());
			financeVoucherInfoDO.setCompanyName(entity.getCompanyName());
			financeVoucherInfoDO.setMakeBy(entity.getMakeBy());
			financeVoucherInfoDO.setVoucherType(entity.getVoucherType());
			financeVoucherInfoDO.setBookTime(entity.getBookTime());
			financeVoucherInfoDO.setBusinessTime(entity.getBusinessTime());
			financeVoucherInfoDO.setAnnual(entity.getAnnual());
			financeVoucherInfoDO.setAnnualCode(entity.getAnnualCode());
			return financeVoucherInfoDO;
		}).collect(Collectors.toList());
		return list;
	}

	private List<FinanceVoucherSummaryVO> voucherMerge(List<FinanceVoucherSummaryVO> voList) {
		return Optional.ofNullable(voList).orElse(new ArrayList<>()).stream().collect(Collectors.toMap(FinanceVoucherSummaryVO::getApartmentId, a -> a, (o1, o2) -> {
			o1.getDetailRespVOList().addAll(o2.getDetailRespVOList());
			return o1;
		})).values().stream().collect(Collectors.toList());
	}

	//凭证发票费项组装
	private List<FinanceVoucherDetailRespVO> voucherInvoiceFeesConvert(List<FinanceCertificateConfigDO> certificateConfigDOList, RentOrderExtVO rentOrderExtVO, Date endDate) {
		List<FinanceVoucherDetailRespVO> financeVoucherDetailRespVOList = new ArrayList<>();
		if (!rentOrderExtVO.getFeeDetail().isEmpty()) {
			for (int i = 0; i < rentOrderExtVO.getFeeDetail().size(); i++) {
				OrderFeeDetailVO orderFeeDetailVO = rentOrderExtVO.getFeeDetail().get(i);
				List<FinanceVoucherDetailRespVO> taxDetailList = taxDetailBean(certificateConfigDOList, rentOrderExtVO, DateUtil.format(endDate, DatePattern.NORM_DATE_PATTERN), orderFeeDetailVO.getFeeName(), orderFeeDetailVO.getAmount());
				financeVoucherDetailRespVOList.addAll(taxDetailList);
			}
		}
		return financeVoucherDetailRespVOList;
	}


	//凭证信息组装
	private static FinanceVoucherSummaryVO voucherSummaryVOConvert(String payMethod, Date endDate, FinanceCertificateConfigDO configDO, Integer voucherType) {
		Calendar cal = Calendar.getInstance();
		FinanceVoucherSummaryVO financeVoucherSummaryVO = new FinanceVoucherSummaryVO();
		financeVoucherSummaryVO.setVoucherType(voucherType);
		//根据门店ID组装信息
		financeVoucherSummaryVO.setPayment(payMethod);
		financeVoucherSummaryVO.setApartmentId(configDO.getApartmentId());
		financeVoucherSummaryVO.setApartmentName(configDO.getApartmentName());
		financeVoucherSummaryVO.setCompanyCode(configDO.getCompanyCode());
		financeVoucherSummaryVO.setCompanyName(configDO.getCompanyName());
		financeVoucherSummaryVO.setMakeBy(configDO.getMakeBy());
		financeVoucherSummaryVO.setBookTime(DateUtil.format(endDate, DatePattern.NORM_DATE_PATTERN));
		financeVoucherSummaryVO.setBusinessTime(DateUtil.format(endDate, DatePattern.NORM_DATE_PATTERN));
		financeVoucherSummaryVO.setAnnual(DateUtil.format(endDate, DatePattern.NORM_YEAR_PATTERN));
		financeVoucherSummaryVO.setAnnualCode(String.valueOf(cal.get(Calendar.MONTH) + 1));
		return financeVoucherSummaryVO;
	}

	// 收款凭证收款方式组装
	private static List<FinanceVoucherDetailRespVO> voucherPaymentMethodConvert(FinanceVoucherSummaryVO financeVoucherSummaryVO, List<FinanceVoucherDetailRespVO> detailRespVOList, List<FinanceVoucherDetailRespVO> paymentDetails) {
		paymentDetails.stream().forEach(entity -> {
			if (FinanceVoucherPaymentTypeMapperEnum.find(financeVoucherSummaryVO.getPayment()).getVoucherName().equals(entity.getFeeName())) {
				BigDecimal sumAmount = detailRespVOList.stream().map(FinanceVoucherDetailRespVO::getDebitAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
				entity.setOriginalAmount(sumAmount);
			}
		});
		return paymentDetails;
	}


	// 收款凭证收款方式bean组装
	private static List<FinanceVoucherDetailRespVO> detailPaymentMethodBean(List<FinanceCertificateConfigDO> certificateConfigDOList, RentOrderExtVO rentOrderExtVO, String endTime) {
		FinanceCertificateConfigDO feeConfig = Optional.ofNullable(certificateConfigDOList).orElse(new ArrayList<>()).stream().filter(entity -> entity.getAccountType() == 1 && entity.getApartmentId().equals(rentOrderExtVO.getApartmentId())).findFirst().get();
		List<FinanceVoucherDetailRespVO> financeVoucherDetailRespVOList = new ArrayList<>();

		FinanceVoucherDetailRespVO vo1 = FinanceVoucherDetailRespVO.builder()
				.seq(1)
				.entryDc(FinanceVoucherJieDaiTypeEnum.JIE.getCode())
				.feeName(FinanceVoucherPaymentTypeEnum.DSFZF.getName())
				.accountName(FinanceVoucherPaymentTypeEnum.DSFZF.getRemark())
				.accountNumber(FinanceVoucherPaymentTypeEnum.DSFZF.getCode())
				.voucherAbstract(feeConfig.getApartmentName() + endTime + "收款")
				.originalAmount(BigDecimal.ZERO)
				.debitAmount(BigDecimal.ZERO)
				//金融机构
				.accountProject1(feeConfig.getAccountProject1())
				.accountProjectCode1(feeConfig.getAccountProjectCode1())
				.accountProjectName1(feeConfig.getAccountProjectName1())
				.build();
		FinanceVoucherDetailRespVO vo2 = FinanceVoucherDetailRespVO.builder()
				.seq(2)
				.entryDc(FinanceVoucherJieDaiTypeEnum.JIE.getCode())
				.feeName(FinanceVoucherPaymentTypeEnum.YHZZ.getName())
				.accountName(FinanceVoucherPaymentTypeEnum.YHZZ.getRemark())
				.accountNumber(FinanceVoucherPaymentTypeEnum.YHZZ.getCode())
				.voucherAbstract(feeConfig.getApartmentName() + endTime + "收款")
				.originalAmount(BigDecimal.ZERO)
				.debitAmount(BigDecimal.ZERO)
				//金融机构
				.accountProject1(feeConfig.getAccountProject1())
				.accountProjectCode1(feeConfig.getAccountProjectCode1())
				.accountProjectName1(feeConfig.getAccountProjectName1())
				//银行账户
				.accountProject2(feeConfig.getAccountProject5())
				.accountProjectCode2(feeConfig.getAccountProjectCode5())
				.accountProjectName2(feeConfig.getAccountProjectName5())
				.build();
		FinanceVoucherDetailRespVO vo3 = FinanceVoucherDetailRespVO.builder()
				.seq(3)
				.entryDc(FinanceVoucherJieDaiTypeEnum.JIE.getCode())
				.feeName(FinanceVoucherPaymentTypeEnum.XJ.getName())
				.accountName(FinanceVoucherPaymentTypeEnum.XJ.getRemark())
				.accountNumber(FinanceVoucherPaymentTypeEnum.XJ.getCode())
				.voucherAbstract(feeConfig.getApartmentName() + endTime + "收款")
				.originalAmount(BigDecimal.ZERO)
				.debitAmount(BigDecimal.ZERO)
				.build();
		financeVoucherDetailRespVOList.add(vo1);
		financeVoucherDetailRespVOList.add(vo2);
		financeVoucherDetailRespVOList.add(vo3);
		return financeVoucherDetailRespVOList;

	}


	// 收款凭证费项组装
	private static List<FinanceVoucherDetailRespVO> voucherFessConvert(List<FinanceCertificateConfigDO> certificateConfigDOList, RentOrderExtVO rentOrderExtVO, String endTime) {

		List<FinanceVoucherDetailRespVO> financeVoucherDetailRespVOList = new ArrayList<>();
		if (!rentOrderExtVO.getFeeDetail().isEmpty()) {
			for (int i = 0; i < rentOrderExtVO.getFeeDetail().size(); i++) {
				OrderFeeDetailVO orderFeeDetailVO = rentOrderExtVO.getFeeDetail().get(i);
				FinanceCertificateConfigDO feeConfig = Optional.ofNullable(certificateConfigDOList).orElse(new ArrayList<>()).stream().filter(entity -> entity.getAccountType() == 1 && entity.getApartmentId().equals(rentOrderExtVO.getApartmentId())).findFirst().get();
				FinanceVoucherDetailRespVO financeVoucherDetailRespVO = FinanceVoucherDetailRespVO.builder()
						.entryDc(FinanceVoucherJieDaiTypeEnum.DAI.getCode())
						.feeName(FinanceVoucherFeeTypeMapperEnum.find(orderFeeDetailVO.getFeeName()).getKdName())
						.accountName(FinanceVoucherFeeTypeEnum.find(FinanceVoucherFeeTypeMapperEnum.find(orderFeeDetailVO.getFeeName()).getKdName()).getRemark())
						.accountNumber(FinanceVoucherFeeTypeEnum.find(FinanceVoucherFeeTypeMapperEnum.find(orderFeeDetailVO.getFeeName()).getKdName()).getCode())
						.voucherAbstract(feeConfig.getApartmentName() + endTime + FinanceVoucherFeeTypeMapperEnum.find(orderFeeDetailVO.getFeeName()).getKdName())
						.originalAmount(BigDecimal.ZERO)
						.debitAmount(orderFeeDetailVO.getAmount())
						//客户
						.accountProject1(feeConfig.getAccountProject1())
						.accountProjectCode1(feeConfig.getAccountProjectCode1())
						.accountProjectName1(feeConfig.getAccountProjectName1())
						//租赁项目
						.accountProject2(feeConfig.getAccountProject2())
						.accountProjectCode2(feeConfig.getAccountProjectCode2())
						.accountProjectName2(feeConfig.getAccountProjectName2())
						//物业地址
						.accountProject3(feeConfig.getAccountProject3())
						.accountProjectCode3(feeConfig.getAccountProjectCode3())
						.accountProjectName3(feeConfig.getAccountProjectName3())
						.build();
				financeVoucherDetailRespVOList.add(financeVoucherDetailRespVO);
			}
		}
		financeVoucherDetailRespVOList = financeVoucherDetailRespVOList.stream().collect(Collectors.toMap(FinanceVoucherDetailRespVO::getFeeName, a -> a, (o1, o2) -> {
			o1.setDebitAmount(o1.getDebitAmount().add(o2.getDebitAmount()));
			return o1;
		})).values().stream().collect(Collectors.toList());
		return financeVoucherDetailRespVOList;
	}

	private static List<FinanceVoucherDetailRespVO> taxDetailBean(List<FinanceCertificateConfigDO> certificateConfigDOList, RentOrderExtVO rentOrderExtVO, String endTime, String feeName, BigDecimal amount) {
		FinanceCertificateConfigDO feeConfig = Optional.ofNullable(certificateConfigDOList).orElse(new ArrayList<>()).stream().filter(entity -> entity.getAccountType() == 1 && entity.getApartmentId().equals(rentOrderExtVO.getApartmentId())).findFirst().get();
		List<FinanceVoucherDetailRespVO> financeVoucherDetailRespVOList = new ArrayList<>();

		String str1 = FinanceVoucherFeeTaxMapperEnum.find(feeName).getKdName();
		String str2 = FinanceVoucherFeeTaxMapperEnum.find(feeName).getNoTax();
		String str3 = FinanceVoucherFeeTaxMapperEnum.find(feeName).getTaxRate();
		Double taxRate = FinanceVoucherIsTaxFeeTypeEnum.find(str3).getTaxRate();
		BigDecimal bhsje = amount.divide(new BigDecimal("1").add(new BigDecimal(taxRate)), BigDecimal.ROUND_CEILING).setScale(2);
		BigDecimal se = amount.subtract(bhsje);
		FinanceVoucherDetailRespVO bean1 = FinanceVoucherDetailRespVO.builder()
				.seq(1)
				.entryDc(FinanceVoucherJieDaiTypeEnum.JIE.getCode())
				.feeName(FinanceVoucherTotalFeeTypeEnum.find(str1).getFeeName())
				.accountName(FinanceVoucherTotalFeeTypeEnum.find(str1).getRemark())
				.accountNumber(FinanceVoucherTotalFeeTypeEnum.find(str1).getCode())
				.voucherAbstract(feeConfig.getApartmentName() + endTime + "开具发票*" + FinanceVoucherTotalFeeTypeEnum.find(str1).getFeeName())
				.originalAmount(amount)
				.debitAmount(BigDecimal.ZERO)
				.accountProject1(feeConfig.getAccountProject1())
				.accountProjectCode1(feeConfig.getAccountProjectCode1())
				.accountProjectName1(feeConfig.getAccountProjectName1())
				.accountProject2(feeConfig.getAccountProject2())
				.accountProjectCode2(feeConfig.getAccountProjectCode2())
				.accountProjectName2(feeConfig.getAccountProjectName2())
				.accountProject3(feeConfig.getAccountProject3())
				.accountProjectCode3(feeConfig.getAccountProjectCode3())
				.accountProjectName3(feeConfig.getAccountProjectName3())
				.build();
		FinanceVoucherDetailRespVO bean2 = FinanceVoucherDetailRespVO.builder()
				.seq(2)
				.entryDc(FinanceVoucherJieDaiTypeEnum.DAI.getCode())
				.feeName(FinanceVoucherNoTaxFeeTypeEnum.find(str2).getFeeName())
				.accountName(FinanceVoucherNoTaxFeeTypeEnum.find(str2).getRemark())
				.accountNumber(FinanceVoucherNoTaxFeeTypeEnum.find(str2).getCode())
				.voucherAbstract(feeConfig.getApartmentName() + endTime + "开具发票*" + FinanceVoucherTotalFeeTypeEnum.find(str1).getFeeName())
				.originalAmount(BigDecimal.ZERO)
				.debitAmount(bhsje)
				.accountProject1(feeConfig.getAccountProject1())
				.accountProjectCode1(feeConfig.getAccountProjectCode1())
				.accountProjectName1(feeConfig.getAccountProjectName1())
				.accountProject2(feeConfig.getAccountProject2())
				.accountProjectCode2(feeConfig.getAccountProjectCode2())
				.accountProjectName2(feeConfig.getAccountProjectName2())
				.accountProject3(feeConfig.getAccountProject3())
				.accountProjectCode3(feeConfig.getAccountProjectCode3())
				.accountProjectName3(feeConfig.getAccountProjectName3())
				.build();
		FinanceVoucherDetailRespVO bean3 = FinanceVoucherDetailRespVO.builder()
				.seq(3)
				.entryDc(FinanceVoucherJieDaiTypeEnum.DAI.getCode())
				.feeName(FinanceVoucherIsTaxFeeTypeEnum.find(str3).getFeeName())
				.accountName(FinanceVoucherIsTaxFeeTypeEnum.find(str3).getRemark())
				.accountNumber(FinanceVoucherIsTaxFeeTypeEnum.find(str3).getCode())
				.voucherAbstract(feeConfig.getApartmentName() + endTime + "开具发票*" + FinanceVoucherTotalFeeTypeEnum.find(str1).getFeeName())
				.originalAmount(BigDecimal.ZERO)
				.debitAmount(se)
				.build();

		financeVoucherDetailRespVOList.add(bean1);
		financeVoucherDetailRespVOList.add(bean2);
		financeVoucherDetailRespVOList.add(bean3);
		return financeVoucherDetailRespVOList;

	}


	/**
	 * 生成金蝶凭证请求号
	 *
	 * @return 凭证请求号
	 */
	public static String voucherNo() {
		return String.format("%s%s%04d", "KD", DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_MS_PATTERN), (int) VOUCHER_NO_SEQ.getAndIncrement() % 10000);
	}

	private static Date getEndOfDay(Date date) {
		LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
		LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX);
		return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
	}

	private static Date getStartOfDay(Date date) {
		LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
		LocalDateTime endOfDay = localDateTime.with(LocalTime.MIN);
		return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
	}

	public static BigDecimal getAfterTaxSum(BigDecimal sum, BigDecimal taxRat) {
		if (sum == null) return BigDecimal.ZERO;
		if (taxRat == null) taxRat = BigDecimal.ZERO;
		return sum.divide(BigDecimal.ONE.add(taxRat.multiply(BigDecimal.valueOf(0.01))), 2, RoundingMode.HALF_UP).stripTrailingZeros();
	}
}
